package com.genersoft.iot.vmp.external;

import com.alibaba.fastjson2.JSONObject;
import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.VersionInfo;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.security.JwtUtils;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
import com.genersoft.iot.vmp.gb28181.service.*;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.ILogService;
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.service.bean.LogFileInfo;
import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy;
import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService;
import com.genersoft.iot.vmp.streamPush.bean.StreamPush;
import com.genersoft.iot.vmp.streamPush.service.IStreamPushService;
import com.genersoft.iot.vmp.vmanager.bean.*;
import com.github.pagehelper.PageInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.ibatis.annotations.Options;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 外部接口控制器
 *
 * @Author: 陈江灿
 * @CreateTime: 2025-02-10
 */
@Tag(name = "外部接口控制器")
@RestController
@RequestMapping("/external")
@Slf4j
public class ExternalController {

    @Autowired
    private VersionInfo versionInfo;

    @Autowired
    private SipConfig sipConfig;

    @Autowired
    private UserSetting userSetting;

    @Value("${server.port}")
    private int serverPort;

    @Autowired
    private IDeviceService deviceService;

    @Autowired
    private IDeviceChannelService channelService;

    @Autowired
    private IStreamPushService pushService;

    @Autowired
    private IStreamProxyService proxyService;

    @Autowired
    private IMediaServerService mediaServerService;

    @Autowired
    private ICloudRecordService cloudRecordService;

    @Autowired
    private IDeviceChannelService deviceChannelService;

    @Autowired
    private IPlayService playService;

    @Autowired
    private DeferredResultHolder resultHolder;

    @Autowired
    private IInviteStreamService inviteStreamService;

    @Autowired
    private ILogService logService;


    /**
     * 获取系统配置信息
     *
     * @return
     */
    @GetMapping(value = "/system/configInfo")
    @ResponseBody
    public SystemConfigInfo getConfigInfo() {
        SystemConfigInfo systemConfigInfo = new SystemConfigInfo();
        systemConfigInfo.setVersion(versionInfo.getVersion());
        systemConfigInfo.setSip(sipConfig);
        systemConfigInfo.setAddOn(userSetting);
        systemConfigInfo.setServerPort(serverPort);
        return systemConfigInfo;
    }

    /**
     * 获取负载信息
     *
     * @return
     */
    @GetMapping(value = "/resource/info")
    @ResponseBody
    public ResourceInfo getResourceInfo() {
        ResourceInfo result = new ResourceInfo();
        ResourceBaseInfo deviceInfo = deviceService.getOverview();
        result.setDevice(deviceInfo);
        ResourceBaseInfo channelInfo = channelService.getOverview();
        result.setChannel(channelInfo);
        ResourceBaseInfo pushInfo = pushService.getOverview();
        result.setPush(pushInfo);
        ResourceBaseInfo proxyInfo = proxyService.getOverview();
        result.setProxy(proxyInfo);
        return result;
    }

    /**
     * 分页查询国标设备
     *
     * @param page
     * @param count
     * @param query
     * @param status
     * @return
     */
    @Parameter(name = "page", description = "当前页", required = true)
    @Parameter(name = "count", description = "每页查询数量", required = true)
    @Parameter(name = "query", description = "搜索", required = false)
    @Parameter(name = "status", description = "状态", required = false)
    @GetMapping("/devices/page")
    @Options()
    public PageInfo<Device> devices(int page, int count, String query, Boolean status) {
        if (ObjectUtils.isEmpty(query)) {
            query = null;
        }
        return deviceService.getAll(page, count, query, status);
    }

    /**
     * 推流列表查询
     *
     * @param page
     * @param count
     * @param query
     * @param pushing
     * @param mediaServerId
     * @return
     */
    @GetMapping(value = "/push/list")
    @ResponseBody
    @Parameter(name = "page", description = "当前页")
    @Parameter(name = "count", description = "每页查询数量")
    @Parameter(name = "query", description = "查询内容")
    @Parameter(name = "pushing", description = "是否正在推流")
    @Parameter(name = "mediaServerId", description = "流媒体ID")
    public PageInfo<StreamPush> pushList(@RequestParam(required = false) Integer page,
                                         @RequestParam(required = false) Integer count,
                                         @RequestParam(required = false) String query,
                                         @RequestParam(required = false) Boolean pushing,
                                         @RequestParam(required = false) String mediaServerId) {

        if (ObjectUtils.isEmpty(query)) {
            query = null;
        }
        if (ObjectUtils.isEmpty(mediaServerId)) {
            mediaServerId = null;
        }
        PageInfo<StreamPush> pushList = pushService.getPushList(page, count, query, pushing, mediaServerId);
        return pushList;
    }

    /**
     * 分页查询流代理
     *
     * @param page
     * @param count
     * @param query
     * @param pulling
     * @param mediaServerId
     * @return
     */
    @Parameter(name = "page", description = "当前页")
    @Parameter(name = "count", description = "每页查询数量")
    @Parameter(name = "query", description = "查询内容")
    @Parameter(name = "pulling", description = "是否正在拉流")
    @Parameter(name = "mediaServerId", description = "流媒体ID")
    @GetMapping(value = "/proxy/list")
    @ResponseBody
    public PageInfo<StreamProxy> proxyList(@RequestParam(required = false) Integer page,
                                           @RequestParam(required = false) Integer count,
                                           @RequestParam(required = false) String query,
                                           @RequestParam(required = false) Boolean pulling,
                                           @RequestParam(required = false) String mediaServerId) {

        if (ObjectUtils.isEmpty(mediaServerId)) {
            mediaServerId = null;
        }
        if (ObjectUtils.isEmpty(query)) {
            query = null;
        }
        return proxyService.getAll(page, count, query, pulling, mediaServerId);
    }

    /**
     * 分页查询云端录像
     *
     * @param query
     * @param app
     * @param stream
     * @param page
     * @param count
     * @param startTime
     * @param endTime
     * @param mediaServerId
     * @param callId
     * @return
     */
    @ResponseBody
    @GetMapping("/cloud/record/list")
    @Parameter(name = "query", description = "检索内容", required = false)
    @Parameter(name = "app", description = "应用名", required = false)
    @Parameter(name = "stream", description = "流ID", required = false)
    @Parameter(name = "page", description = "当前页", required = true)
    @Parameter(name = "count", description = "每页查询数量", required = true)
    @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
    @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
    @Parameter(name = "mediaServerId", description = "流媒体ID，置空则查询全部流媒体", required = false)
    @Parameter(name = "callId", description = "每次录像的唯一标识，置空则查询全部流媒体", required = false)
    public PageInfo<CloudRecordItem> openRtpServer(@RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId

    ) {
        log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId);

        List<MediaServer> mediaServers;
        if (!org.apache.commons.lang3.ObjectUtils.isEmpty(mediaServerId)) {
            mediaServers = new ArrayList<>();
            MediaServer mediaServer = mediaServerService.getOne(mediaServerId);
            if (mediaServer == null) {
                throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体: " + mediaServerId);
            }
            mediaServers.add(mediaServer);
        } else {
            mediaServers = mediaServerService.getAllOnlineList();
        }
        if (mediaServers.isEmpty()) {
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
        }
        if (query != null && org.apache.commons.lang3.ObjectUtils.isEmpty(query.trim())) {
            query = null;
        }
        if (app != null && org.apache.commons.lang3.ObjectUtils.isEmpty(app.trim())) {
            app = null;
        }
        if (stream != null && org.apache.commons.lang3.ObjectUtils.isEmpty(stream.trim())) {
            stream = null;
        }
        if (startTime != null && org.apache.commons.lang3.ObjectUtils.isEmpty(startTime.trim())) {
            startTime = null;
        }
        if (endTime != null && org.apache.commons.lang3.ObjectUtils.isEmpty(endTime.trim())) {
            endTime = null;
        }
        if (callId != null && org.apache.commons.lang3.ObjectUtils.isEmpty(callId.trim())) {
            callId = null;
        }
        return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId);
    }

    /**
     * 获取播放地址
     *
     * @param recordId
     * @return
     */
    @ResponseBody
    @GetMapping("/cloud/record/play/path")
    @Operation(summary = "获取播放地址")
    @Parameter(name = "recordId", description = "录像记录的ID", required = true)
    public DownloadFileInfo getPlayUrlPath(@RequestParam(required = true) Integer recordId) {
        return cloudRecordService.getPlayUrlPath(recordId);
    }

    /**
     * 修改数据流传输模式
     *
     * @param deviceId
     * @param streamMode
     */
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @Parameter(name = "streamMode", description = "数据流传输模式, 取值：" +
            "UDP（udp传输），TCP-ACTIVE（tcp主动模式,暂不支持），TCP-PASSIVE（tcp被动模式）", required = true)
    @PostMapping("/transport/{deviceId}/{streamMode}")
    public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode) {
        Device device = deviceService.getDeviceByDeviceId(deviceId);
        device.setStreamMode(streamMode);
        deviceService.updateCustomDevice(device);
    }

    /**
     * 分页查询通道
     *
     * @param deviceId
     * @param page
     * @param count
     * @param query
     * @param online
     * @param channelType
     * @return
     */
    @GetMapping("/devices/{deviceId}/channels")
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @Parameter(name = "page", description = "当前页", required = true)
    @Parameter(name = "count", description = "每页查询数量", required = true)
    @Parameter(name = "query", description = "查询内容")
    @Parameter(name = "online", description = "是否在线")
    @Parameter(name = "channelType", description = "设备/子目录-> false/true")
    public PageInfo<DeviceChannel> channels(@PathVariable String deviceId,
                                            int page, int count,
                                            @RequestParam(required = false) String query,
                                            @RequestParam(required = false) Boolean online,
                                            @RequestParam(required = false) Boolean channelType) {
        if (ObjectUtils.isEmpty(query)) {
            query = null;
        }
        return deviceChannelService.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count);
    }

    /**
     * 使用ID查询国标设备
     * @param deviceId
     * @return
     */
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @GetMapping("/devicesById/{deviceId}")
    public Device devices(@PathVariable String deviceId){
        return deviceService.getDeviceByDeviceId(deviceId);
    }

    /**
     * 开启/关闭通道的音频
     * @param channelId
     * @param audio
     */
    @Parameter(name = "channelId", description = "通道的数据库ID", required = true)
    @Parameter(name = "audio", description = "开启/关闭音频", required = true)
    @PostMapping("/channel/audio")
    public void changeAudio(Integer channelId, Boolean audio){
        Assert.notNull(channelId, "通道的数据库ID不可为NULL");
        Assert.notNull(audio, "开启/关闭音频不可为NULL");
        deviceChannelService.changeAudio(channelId, audio);
    }

    /**
     * 修改通道的码流类型
     * @param channel
     */
    @PostMapping("/channel/stream/identification/update/")
    public void updateChannelStreamIdentification(DeviceChannel channel){
        deviceChannelService.updateChannelStreamIdentification(channel);
    }

    /**
     * 开始点播
     * @param request
     * @param deviceId
     * @param channelId
     * @return
     */
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @Parameter(name = "channelId", description = "通道国标编号", required = true)
    @GetMapping("/start/{deviceId}/{channelId}")
    public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId,
                                                         @PathVariable String channelId) {

        log.info("[开始点播] deviceId：{}, channelId：{}, ", deviceId, channelId);
        Assert.notNull(deviceId, "设备国标编号不可为NULL");
        Assert.notNull(channelId, "通道国标编号不可为NULL");
        // 获取可用的zlm
        Device device = deviceService.getDeviceByDeviceId(deviceId);
        Assert.notNull(deviceId, "设备不存在");
        DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId);
        Assert.notNull(channel, "通道不存在");
        MediaServer newMediaServerItem = playService.getNewMediaServerItem(device);

        RequestMessage requestMessage = new RequestMessage();
        String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
        requestMessage.setKey(key);
        String uuid = UUID.randomUUID().toString();
        requestMessage.setId(uuid);
        DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());

        result.onTimeout(()->{
            log.info("[点播等待超时] deviceId：{}, channelId：{}, ", deviceId, channelId);
            // 释放rtpserver
            WVPResult<StreamInfo> wvpResult = new WVPResult<>();
            wvpResult.setCode(ErrorCode.ERROR100.getCode());
            wvpResult.setMsg("点播超时");
            requestMessage.setData(wvpResult);
            resultHolder.invokeAllResult(requestMessage);
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId());
            deviceChannelService.stopPlay(channel.getId());
        });

        // 录像查询以channelId作为deviceId查询
        resultHolder.put(key, uuid, result);

        playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, streamInfo) -> {
            WVPResult<StreamContent> wvpResult = new WVPResult<>();
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                wvpResult.setCode(ErrorCode.SUCCESS.getCode());
                wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());

                if (streamInfo != null) {
                    if (userSetting.getUseSourceIpAsStreamIp()) {
                        streamInfo=streamInfo.clone();//深拷贝
                        String host;
                        try {
                            URL url=new URL(request.getRequestURL().toString());
                            host=url.getHost();
                        } catch (MalformedURLException e) {
                            host=request.getLocalAddr();
                        }
                        streamInfo.channgeStreamIp(host);
                    }
                    if (!ObjectUtils.isEmpty(newMediaServerItem.getTranscodeSuffix()) && !"null".equalsIgnoreCase(newMediaServerItem.getTranscodeSuffix())) {
                        streamInfo.setStream(streamInfo.getStream() + "_" + newMediaServerItem.getTranscodeSuffix());
                    }
                    wvpResult.setData(new StreamContent(streamInfo));
                }else {
                    wvpResult.setCode(code);
                    wvpResult.setMsg(msg);
                }
            }else {
                wvpResult.setCode(code);
                wvpResult.setMsg(msg);
            }
            requestMessage.setData(wvpResult);
            // 此处必须释放所有请求
            resultHolder.invokeAllResult(requestMessage);
        });
        return result;
    }

    /**
     * 停止点播
     * @param deviceId
     * @param channelId
     * @return
     */
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @Parameter(name = "channelId", description = "通道国标编号", required = true)
    @GetMapping("/stop/{deviceId}/{channelId}")
    public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) {

        log.debug(String.format("设备预览/回放停止API调用，streamId：%s_%s", deviceId, channelId ));

        if (deviceId == null || channelId == null) {
            throw new ControllerException(ErrorCode.ERROR400);
        }

        Device device = deviceService.getDeviceByDeviceId(deviceId);
        DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId);
        Assert.notNull(device, "设备不存在");
        Assert.notNull(channel, "通道不存在");
        String streamId = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId());
        playService.stop(InviteSessionType.PLAY, device, channel, streamId);
        JSONObject json = new JSONObject();
        json.put("deviceId", deviceId);
        json.put("channelId", channelId);
        return json;
    }

    /**
     * 获取通道同步进度
     * @param deviceId
     * @return
     */
    @GetMapping("/{deviceId}/sync_status")
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) {
        SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
        WVPResult<SyncStatus> wvpResult = new WVPResult<>();
        if (channelSyncStatus == null) {
            wvpResult.setCode(ErrorCode.ERROR100.getCode());
            wvpResult.setMsg("同步不存在");
        }else if (channelSyncStatus.getErrorMsg() != null) {
            wvpResult.setCode(ErrorCode.ERROR100.getCode());
            wvpResult.setMsg(channelSyncStatus.getErrorMsg());
        }else if (channelSyncStatus.getTotal() == null || channelSyncStatus.getTotal() == 0){
            wvpResult.setCode(ErrorCode.SUCCESS.getCode());
            wvpResult.setMsg("等待通道信息...");
        }else {
            wvpResult.setCode(ErrorCode.SUCCESS.getCode());
            wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
            wvpResult.setData(channelSyncStatus);
        }
        return wvpResult;
    }

    /**
     * 同步设备通道
     * @param deviceId
     * @return
     */
    @Parameter(name = "deviceId", description = "设备国标编号", required = true)
    @GetMapping("/devices/{deviceId}/sync")
    public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId){

        if (log.isDebugEnabled()) {
            log.debug("设备通道信息同步API调用，deviceId：" + deviceId);
        }
        Device device = deviceService.getDeviceByDeviceId(deviceId);
        boolean status = deviceService.isSyncRunning(deviceId);
        // 已存在则返回进度
        if (deviceService.isSyncRunning(deviceId)) {
            SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
            WVPResult wvpResult = new WVPResult();
            if (channelSyncStatus.getErrorMsg() != null) {
                wvpResult.setCode(ErrorCode.ERROR100.getCode());
                wvpResult.setMsg(channelSyncStatus.getErrorMsg());
            }else if (channelSyncStatus.getTotal() == null || channelSyncStatus.getTotal() == 0){
                wvpResult.setCode(ErrorCode.SUCCESS.getCode());
                wvpResult.setMsg("等待通道信息...");
            }else {
                wvpResult.setCode(ErrorCode.SUCCESS.getCode());
                wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
                wvpResult.setData(channelSyncStatus);
            }
            return wvpResult;
        }
        deviceService.sync(device);

        WVPResult<SyncStatus> wvpResult = new WVPResult<>();
        wvpResult.setCode(0);
        wvpResult.setMsg("开始同步");
        return wvpResult;
    }

    /**
     * 分页查询日志文件
     * @param query
     * @param startTime
     * @param endTime
     * @return
     */
    @ResponseBody
    @GetMapping("/logList")
    @Parameter(name = "query", description = "检索内容", required = false)
    @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
    @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
    public List<LogFileInfo> queryList(@RequestParam(required = false) String query, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime

    ) {
        if (org.apache.commons.lang3.ObjectUtils.isEmpty(query)) {
            query = null;
        }
        if (org.apache.commons.lang3.ObjectUtils.isEmpty(startTime)) {
            startTime = null;
        }
        if (org.apache.commons.lang3.ObjectUtils.isEmpty(endTime)) {
            endTime = null;
        }
        return logService.queryList(query, startTime, endTime);
    }


    /**
     * 下载指定日志文件
     */
    @ResponseBody
    @GetMapping("/log/file/{fileName}")
    public void downloadFile(HttpServletResponse response, @PathVariable  String fileName) {
        try {
            File file = logService.getFileByName(fileName);
            if (file == null || !file.exists() || !file.isFile()) {
                throw new ControllerException(ErrorCode.ERROR400);
            }
            final InputStream in = Files.newInputStream(file.toPath());
            response.setContentType(MediaType.TEXT_PLAIN_VALUE);
            ServletOutputStream outputStream = response.getOutputStream();
            IOUtils.copy(in, response.getOutputStream());
            in.close();
            outputStream.close();
        } catch (IOException e) {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        }
    }



}
